
import sys
import time
import math

import ogre.renderer.OGRE as OGRE
import ogre.io.OIS as OIS

SHADOWS = True
COMPOSITOR = True
LOG = False

SPEED_MOVE = 0.01
SPEED_ROTATE = 0.1

def debug(s):
    print "[" * 40, s, "]" * 40

def id(*args):
    return "-".join(map(str, args))

class MyListener(OGRE.FrameListener, OGRE.WindowEventListener, OIS.MouseListener, OIS.KeyListener):
    def __init__(self, renderWindow):
        OGRE.FrameListener.__init__(self)
        OGRE.WindowEventListener.__init__(self)
        OIS.MouseListener.__init__(self)
        OIS.KeyListener.__init__(self)

        handle = renderWindow.getCustomAttributeInt("WINDOW")
        self.inputManager = OIS.createPythonInputSystem([("WINDOW", str(handle))])

        self.mouse = self.inputManager.createInputObjectMouse(OIS.OISMouse, True)
        self.mouse.setEventCallback(self)

        self.keyboard = self.inputManager.createInputObjectKeyboard(OIS.OISKeyboard, True)
        self.keyboard.setEventCallback(self)

        OGRE.WindowEventUtilities.addWindowEventListener(renderWindow, self)

        self.timer = OGRE.Timer()
        self.frames = 0
        self.run = True

        self.rotationX, self.rotationY = 0.0, 0.0
        self.moveX, self.moveY, self.moveZ = 0.0, 0.0, 0.0

    def frameStarted(self, event):
        self.keyboard.capture()
        self.mouse.capture()

        # FPS
        ms = self.timer.getMilliseconds()
        if ms > 5000:
            print "FPS", 1000.0 * self.frames / ms
            self.frames = 0
            self.timer.reset()
        else:
            self.frames += 1

        camera.yaw(OGRE.Degree(self.rotationX))
        camera.pitch(OGRE.Degree(self.rotationY))
        camera.moveRelative(OGRE.Vector3(self.moveX, self.moveY, self.moveZ))

        self.rotationX, self.rotationY = 0.0, 0.0

        return True

    def frameEnded(self, event):
        return self.run

    def windowResized(self, renderWindow):
        print "Window Resized"

    def windowClosed(self, renderWindow):
        print "Window Closed"

    def mouseMoved(self, event):
        x, y = event.get_state().X.rel, event.get_state().Y.rel
        self.rotationX, self.rotationY = -x * SPEED_ROTATE, -y * SPEED_ROTATE

    def mousePressed(self, event, id):
        state = event.get_state()
        if state.buttonDown(OIS.MB_Left):
            self.moveZ = -SPEED_MOVE
        elif state.buttonDown(OIS.MB_Right):
            self.moveZ = SPEED_MOVE
        else:
            print buttons

    def mouseReleased(self, event, id):
        self.moveX, self.moveY, self.moveZ = 0.0, 0.0, 0.0

    def keyPressed(self, event):
        key = event.key
        if key == OIS.KC_ESCAPE:
            self.run = False
        elif key == OIS.KC_A:
            self.moveX = -SPEED_MOVE
        elif key == OIS.KC_D:
            self.moveX = SPEED_MOVE
        elif key == OIS.KC_W:
            self.moveZ = -SPEED_MOVE
        elif key == OIS.KC_S:
            self.moveZ = SPEED_MOVE
        elif key == OIS.KC_Q:
            self.moveY = -SPEED_MOVE
        elif key == OIS.KC_E:
            self.moveY = SPEED_MOVE
        else:
            print "Key Pressed", key

    def keyReleased(self, event):
        self.moveX, self.moveY, self.moveZ = 0.0, 0.0, 0.0
        #print "Key Released", event.key
        pass

logManager = OGRE.LogManager()
log = logManager.createLog("example.log", True, LOG, True) # name, default log, debugger output, suppress file output

root = OGRE.Root()

OGRE.ResourceGroupManager.getSingleton().addResourceLocation(".", "FileSystem", "General")

if not root.restoreConfig():
    root.showConfigDialog()

root.initialise(True, "MyExample")
renderWindow = root.getAutoCreatedWindow()

OGRE.TextureManager.getSingleton().setDefaultNumMipmaps(5)
OGRE.ResourceGroupManager.getSingleton().initialiseAllResourceGroups()

sceneManager = root.createSceneManager(OGRE.ST_GENERIC, "SceneManager")

if SHADOWS:
    #sceneManager.setShadowTechnique(OGRE.SHADOWTYPE_STENCIL_MODULATIVE);
    sceneManager.setShadowTechnique(OGRE.SHADOWTYPE_TEXTURE_MODULATIVE);
    sceneManager.setShadowColour(OGRE.ColourValue(0.5, 0.5, 0.5, 1.0))
    sceneManager.setShadowTextureSize(1024)
    #sceneManager.setShadowTextureSettings(1024, 2)

    #sceneManager.setShadowFarDistance(100.0)
    #sceneManager.setShadowFarDistance(100.0)

camera = sceneManager.createCamera("Camera")
camera.setNearClipDistance(0.1)
#camera.setFarClipDistance(100.0)
camera.setPosition(OGRE.Vector3(0.001, 5.0, 0.0)) # TODO why does the camera with exactly 0.0 screws up itself
camera.lookAt(OGRE.Vector3(0.0, 0.0, 0.0))

viewport = renderWindow.addViewport(camera)

# If you do not load the Cg plugin often shaders do not compile, because there is no GLSL/HLSL counterpart, but you do not get a warning that there is no Cg compiler
if COMPOSITOR:
    OGRE.CompositorManager.getSingleton().addCompositor(viewport, "SharpenEdges");
    OGRE.CompositorManager.getSingleton().setCompositorEnabled(viewport, "SharpenEdges", True);

# default ambient light is 1.0, 1.0, 1.0
sceneManager.setAmbientLight(OGRE.ColourValue(0.1, 0.1, 0.1))

#a, b, c = 1.0, 0.0, 0.01
#def l(d, a, b, c):
#    return 1.0 / (a + b * d + c * d * d)

light = sceneManager.createLight("Light")
light.setPosition(OGRE.Vector3(0.0, 5.0, 0.0))
light.setType(OGRE.Light.LT_SPOTLIGHT)
light.setDirection(OGRE.Vector3(0.0, -1.0, 0.0))
light.setDiffuseColour(1.0, 1.0, 1.0)
light.setSpotlightRange(0.0, math.pi / 2.0, 0.5)
#light.setSpecularColour(1.0, 1.0, 1.0)
#light.setAttenuation(100.0, a, b, c)
#light.setAttenuation(1000.0, 0.0, 0.0, 0.0)

#print "Light D=0", l(0.0, a, b, c)
#print "Light D=1", l(1.0, a, b, c)
#print "Light D=5", l(5.0, a, b, c)
#print "Light D=10", l(10.0, a, b, c)
#print "Light D=20", l(20.0, a, b, c)

nodeRoot = sceneManager.getRootSceneNode()

# plane has no diffuse lighting because spotlight does not reach the edges (in fixed function pipeline)
entityPlane = sceneManager.createEntity("Plane", "Plane.mesh")
entityPlane.setCastShadows(False)
nodePlane = nodeRoot.createChildSceneNode()
nodePlane.setPosition(OGRE.Vector3(0.0, 0.0, 0.0))
nodePlane.attachObject(entityPlane)

positions = [-4.0, -2.0, 0.0, 2.0, 4.0]
#positions = [0.0]
for x in positions:
    for z in positions:
        entityBlock = sceneManager.createEntity(id("Block", x, z), "Sphere.mesh")
        nodeBlock = nodeRoot.createChildSceneNode()
        nodeBlock.setPosition(OGRE.Vector3(x, 1.0, z))
        nodeBlock.attachObject(entityBlock)

#entityPlane.setMaterialName("Violet")

listener = MyListener(renderWindow)
root.addFrameListener(listener)

while True:
    OGRE.WindowEventUtilities().messagePump()
    if not root.renderOneFrame():
        break
    #time.sleep(0.01)

del root # python-ogre crashs if we do not delete root here, although python would clear it later anyway
